Spring Security

Eljutottunk rövidke sorozazunk utolsó darabjához, a Security részhez. A Spring Boot-ban már előre létrehoztak számos, könnyen beépíthető security modult, amelyet weboldalunk biztonsági kezeléséhez is nyugodt szívvel fel tudunk használni. Ebbe bele lehet érteni többek között a jelszavas autentikálást, szerepkörhöz kötött oldal-eléréseket, vagy akár az adatbázis mező szintjéig is létrehozthatunk megkötéseket, azaz azt, hogy egy mezőt csak adott szerepkörrel rendelkező user írhat felül. Vagy akár azt is megmondhatjuk, hogy adott fájlformátumú fájlokat nem tölthet le egy mezei user a weboldalról.

Mi is kell nekünk a Security beüzemeléséhez? Ahogy a Spring kapcsán már megszokhattuk, rendkívül "nehéz" beállítások kellenek: egyszerűen a POM.XML-be be kell kerülnie dependencyként az org.springframework.security-nek, ezen kívül a Thymeleaf-nek is van egy Security kiterjesztése, amelyet szintén be kell importálni (thymeleaf-extras-springsecurity4). Ennyi. Ha ez bekerült a POM-ba, és a weboldalt meghívjuk (a szerver újraindítása után), akkor usernevet és jelszót kér, ezzel ellenőrizhetjük, hogy igen, működik. Mivel ez nekünk még kevés, kicsit menjünk tovább. Első körben hozzunk létre egy Config mappát, amely a projektünkhöz kapcsolódó beállításokat fogja tartalmazni. A SecurityConf-ban látható, hogy a konfigurációs osztályunkat elláttuk a @EnableWebSecurity annotációval, amely szükséges ahhoz, hogy a websecurity-hez kapcsolódó Spring-es lehetőségeket kihasználjuk. Ennek keretében még a WebSecurityConfigurerAdapter ősosztály is kiterjesztjük, amely számos, már megírt security metódus felhasználását teszi nekünk lehetővé.

Az egyik legfontosabb része a dolognak a protected void configure(HttpSecurity httpSec) throws Exception metódusunk lesz. Ez az a terület, ahol magát a szerver viselkedését tudjuk befolyásolni, itt tudjuk megmondani többek között azt, hogy az egyes felhasználói jogosultságokkal milyen oldalakat érhetnek el, milyen fájlokhoz férhetnek hozzá, stb. A legbiztonságosabb megoldás, ha már az elején azt a szabályt definiáljuk, hogy a szerveren MINDEN le legyen korlátozva, azaz semmihez se lehessen alapból hozzáérni. Ezt követően pedig szépen egyenként minden oldalt, fájlt, akármit engedélyezünk adott szerepkörnek. Ezzel nagyfokú biztonsági kockázattól tudjuk megóvni magunkat, hiszen a későbbiekben, ha valamit felteszünk a szerverre, nem kell attól félni, hogy elfelejtettük az adott oldalt mondjuk lekorlátozni. Ezt el tudjuk érni a httpSec.authorizeRequests().anyRequest().authenticated() paranccsal.

Nézzük a kódokat egyenként: .authorizeRequests() - ez azt jelenti, hogy a továbbiakban felsoroltak elérést engedélyezzük.

.antMatchers("/createposts/**") - a Spring figyeljen minden olyan lekérést, amely a /createposts/**-ra és annak aloldalaira irányul, majd az .authenticated() sorral pedig azt jelezzük, hogy minden kérést, amely ezekre az oldalakra jön, azokat autentikálni kell. Ezt követően a .formLogin() elérését is lehetővé tesszük úgy, hogy automatikusan a login.html oldalra irányítsa a felhasználót: .loginPage("/login"). Ezt pedig mindenki számára engedélyezzük a .permitAll() paranccsal. Ugyanezt aztán eljátsszuk a logout kapcsán is. Természetesen azzal, hogy a Thymeleaf security dependency-t beimportáltuk korábban, azzal a HTML oldalon is lehetővé tesszük az ehhez való kapcsolódást, azaz azt, hogy a HTML oldali form felületen elküldött üzenetet itt, a SecurityConf-ban feldolgozzuk.

Ahhoz, hogy a view réteg és a Controller réteg közötti kapcsolat meglegyen, ahhoz kell még valami, mégpedig egy, a kapcsolatot létrehozó osztály, a WebConfig osztály. Itt a registry kerül meghívásra: maga a registry az, ami a kapcsolatokat tárolja a két réteg között. A registry.addViewController("/login").setViewName("auth/login"); parancs esetében azt mondjuk meg, hogy a SecurityConf-ban előzőleg beállított .loginPage("/login") elérésekor magát a html oldalt az auth/login mappában keresse meg az alkalmazás. Mi kell még nekünk a kapcsolathoz a Front-end oldalon? Egyszerűen csak annyi, hogy a login.html oldalon a form felületnél szerepelnie kell a login-ra való hivatkozásnak eképpen: th:action="@{/login}". Ezzel a form felületen elküldött adatok átkerülnek a Back-end oldalára. Ezt követően ha a Spring security error-t ad vissza, azt a Thymeleaf feldolgozza a th:if="${param.error}" résznél, és kiírja hogy "Hibás felhasználói név vagy jelszó". Ha pedig logout paramétert ad vissza, akkor a th:if="${param.logout}" pedig a "Sikeresen kijelentkeztél" szöveget adja vissza.

Ez eddig szuper, itt az idő, hogy az adatbázisban eltárolt felhasználók és jogosultságok alapján a fentieket használni is tudjuk. A korábbi részekben már bemutattam, hogy létre lettek hozva adatbázis oldali táblák (users, roles, users_roles), az ahhoz kapcsolódó repository rétegről is beszéltünk. Ami fontos az a service rétegben lévő UserDetailsImpl, amely a Spring-ben már alapból meglévő UserDetails interface-t implementáljuk. Ez az interface a userek adatainak feldolgozását végzi, implementálásával pedig megmondjuk, hogy azokat az adatokat milyen módon dolgozza fel a Spring security. Itt létrehozunk egy HashSet-et: Set grantedAuthorities = new HashSet<>(); Ebbe a hashset-be aztán egy for ciklussal beletesszük a Role táblában szereplő role-okat a neki átadott username-hez kapcsolódóan. Ezeket pedig átadja a SimpleGrantedAuthority-nak, amely a Spring security feldolgozásához szükséges.

A sorozat elején már átvettük, hogy az adatbázisban a password-öt kódolva tároljuk. Ennek feldolgozásához szükséges a SecurityConf osztályban a BCryptPasswordEncoder használata, és ahhoz kapcsolódóan a auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());. Ha ezt használjuk, akkor a kódolt adatokat is fel tudjuk dolgozni és be tudjuk léptetni a felhasználót a számára engedélyezett oldalra.

Nagy vonalakban alapként elegendő ennyi. Amit látni kell, hogy a Spring Security egy nagyon tág valami, mi éppen csak a felszínt súroltuk. A mini-sorozatunk ezzel lezárult, mostantól a Java Spring más vizeire evezünk. A sorozat eddigi részeinek elkészítésében nagy segítségemre volt a San Fransiscoból Jöttem weboldal video-tananyaga, amelyet mindenki számára ajánlani tudok.